/**
 * 项目结构分析器 - 学习整个 C++ 工程的目录结构和测试模式
 * 
 * 功能：
 * 1. 扫描源文件目录结构
 * 2. 扫描测试文件目录结构和命名模式
 * 3. 收集示例测试文件作为参考
 * 4. 分析 GN 文件的组织模式
 */

import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import { Logger } from '../../../../src/logger/Logger';

export interface ProjectStructure {
    // 源文件信息
    sourceFile: {
        path: string;
        relativePath: string;
        directory: string;
        fileName: string;
    };
    
    // 目录结构信息
    directoryStructure: {
        sourceDir: string;
        testDir: string;
        relativeSourceDir: string;
        relativeTestDir: string;
    };
    
    // 现有测试文件示例（用于学习命名和编写模式）
    existingTestExamples: Array<{
        filePath: string;
        relativePath: string;
        fileName: string;
        content?: string;  // 可选：文件内容（如果文件不大）
    }>;
    
    // GN 文件示例（用于学习配置模式）
    existingGNExamples: Array<{
        filePath: string;
        relativePath: string;
        content: string;
        type: 'source' | 'test';
    }>;
    
    // 项目模式分析
    patterns: {
        testFileNaming: string;  // 测试文件命名模式，如 "*_test.cpp"
        testDirectory: string;   // 测试目录模式，如 "test/"
        gnFileLocations: string[];  // GN 文件位置
    };
}

export class ProjectStructureAnalyzer {
    private workspaceRoot: string;
    
    constructor() {
        const workspaceFolders = vscode.workspace.workspaceFolders;
        if (!workspaceFolders || workspaceFolders.length === 0) {
            throw new Error('没有打开的工作区');
        }
        this.workspaceRoot = workspaceFolders[0].uri.fsPath;
    }
    
    /**
     * 分析项目结构
     */
    async analyzeProjectStructure(sourceFilePath: string): Promise<ProjectStructure> {
        Logger.info(`[ProjectStructureAnalyzer] 📊 开始分析项目结构`);
        Logger.info(`[ProjectStructureAnalyzer]    工作区: ${this.workspaceRoot}`);
        Logger.info(`[ProjectStructureAnalyzer]    源文件: ${sourceFilePath}`);
        
        const structure: ProjectStructure = {
            sourceFile: this.analyzeSourceFile(sourceFilePath),
            directoryStructure: await this.analyzeDirectoryStructure(sourceFilePath),
            existingTestExamples: [],
            existingGNExamples: [],
            patterns: {
                testFileNaming: '',
                testDirectory: '',
                gnFileLocations: []
            }
        };
        
        // 收集现有测试文件示例
        structure.existingTestExamples = await this.collectTestExamples(
            structure.directoryStructure.testDir
        );
        
        // 收集 GN 文件示例
        structure.existingGNExamples = await this.collectGNExamples(
            structure.directoryStructure.sourceDir,
            structure.directoryStructure.testDir
        );
        
        // 分析项目模式
        structure.patterns = this.analyzePatterns(structure);
        
        Logger.info(`[ProjectStructureAnalyzer] ✅ 项目结构分析完成`);
        Logger.info(`[ProjectStructureAnalyzer]    找到 ${structure.existingTestExamples.length} 个测试文件示例`);
        Logger.info(`[ProjectStructureAnalyzer]    找到 ${structure.existingGNExamples.length} 个 GN 文件示例`);
        
        return structure;
    }
    
    /**
     * 分析源文件信息
     */
    private analyzeSourceFile(sourceFilePath: string) {
        const relativePath = path.relative(this.workspaceRoot, sourceFilePath);
        const directory = path.dirname(sourceFilePath);
        const fileName = path.basename(sourceFilePath);
        
        return {
            path: sourceFilePath,
            relativePath,
            directory,
            fileName
        };
    }
    
    /**
     * 分析目录结构
     */
    private async analyzeDirectoryStructure(sourceFilePath: string) {
        const sourceDir = path.dirname(sourceFilePath);
        
        // 尝试找到测试目录
        // 1. 同级的 test/ 目录
        let testDir = path.join(sourceDir, 'test');
        
        // 2. 如果不存在，尝试父目录的 test/
        if (!fs.existsSync(testDir)) {
            const parentDir = path.dirname(sourceDir);
            testDir = path.join(parentDir, 'test');
        }
        
        // 3. 如果还不存在，尝试寻找任何包含 test 的目录
        if (!fs.existsSync(testDir)) {
            testDir = await this.findTestDirectory(sourceDir);
        }
        
        const relativeSourceDir = path.relative(this.workspaceRoot, sourceDir);
        const relativeTestDir = path.relative(this.workspaceRoot, testDir);
        
        Logger.info(`[ProjectStructureAnalyzer] 📁 目录结构:`);
        Logger.info(`[ProjectStructureAnalyzer]    源目录: ${relativeSourceDir}`);
        Logger.info(`[ProjectStructureAnalyzer]    测试目录: ${relativeTestDir}`);
        
        return {
            sourceDir,
            testDir,
            relativeSourceDir,
            relativeTestDir
        };
    }
    
    /**
     * 查找测试目录
     */
    private async findTestDirectory(startDir: string): Promise<string> {
        // 向上查找，直到找到包含 test 目录的层级
        let currentDir = startDir;
        const maxDepth = 5;
        let depth = 0;
        
        while (depth < maxDepth) {
            const testDir = path.join(currentDir, 'test');
            if (fs.existsSync(testDir)) {
                return testDir;
            }
            
            const parentDir = path.dirname(currentDir);
            if (parentDir === currentDir) {
                break; // 已到根目录
            }
            
            currentDir = parentDir;
            depth++;
        }
        
        // 如果还是找不到，就使用源文件同级的 test 目录（即使不存在）
        return path.join(startDir, 'test');
    }
    
    /**
     * 收集现有测试文件示例（最多3个，用于学习）
     */
    private async collectTestExamples(testDir: string): Promise<Array<{
        filePath: string;
        relativePath: string;
        fileName: string;
        content?: string;
    }>> {
        Logger.info(`[ProjectStructureAnalyzer] 🔍 收集测试文件示例: ${testDir}`);
        
        if (!fs.existsSync(testDir)) {
            Logger.warn(`[ProjectStructureAnalyzer] ⚠️ 测试目录不存在: ${testDir}`);
            return [];
        }
        
        const examples: Array<{
            filePath: string;
            relativePath: string;
            fileName: string;
            content?: string;
        }> = [];
        
        const testFiles = this.findTestFilesRecursive(testDir, 3); // 最多收集3个示例
        
        for (const testFile of testFiles) {
            const relativePath = path.relative(this.workspaceRoot, testFile);
            const fileName = path.basename(testFile);
            
            // 读取文件内容（只读取小于 10KB 的文件）
            let content: string | undefined;
            try {
                const stats = fs.statSync(testFile);
                if (stats.size < 10 * 1024) { // 10KB
                    content = fs.readFileSync(testFile, 'utf-8');
                    Logger.info(`[ProjectStructureAnalyzer]    ✅ 读取示例: ${fileName} (${stats.size} bytes)`);
                } else {
                    Logger.info(`[ProjectStructureAnalyzer]    ⏩ 跳过大文件: ${fileName} (${stats.size} bytes)`);
                }
            } catch (error) {
                Logger.warn(`[ProjectStructureAnalyzer]    ⚠️ 读取失败: ${fileName}`);
            }
            
            examples.push({
                filePath: testFile,
                relativePath,
                fileName,
                content
            });
        }
        
        return examples;
    }
    
    /**
     * 递归查找测试文件
     */
    private findTestFilesRecursive(dir: string, maxCount: number): string[] {
        const results: string[] = [];
        
        if (!fs.existsSync(dir)) {
            return results;
        }
        
        const files = fs.readdirSync(dir);
        
        for (const file of files) {
            if (results.length >= maxCount) {
                break;
            }
            
            const filePath = path.join(dir, file);
            const stats = fs.statSync(filePath);
            
            if (stats.isDirectory()) {
                // 递归查找子目录
                const subResults = this.findTestFilesRecursive(filePath, maxCount - results.length);
                results.push(...subResults);
            } else if (stats.isFile()) {
                // 检查是否是测试文件
                if (this.isTestFile(file)) {
                    results.push(filePath);
                }
            }
        }
        
        return results;
    }
    
    /**
     * 判断是否是测试文件
     */
    private isTestFile(fileName: string): boolean {
        const testPatterns = [
            /_test\.cpp$/,
            /_test\.cc$/,
            /_unittest\.cpp$/,
            /_unittest\.cc$/,
            /test_.*\.cpp$/,
            /test_.*\.cc$/,
        ];
        
        return testPatterns.some(pattern => pattern.test(fileName));
    }
    
    /**
     * 收集 GN 文件示例
     */
    private async collectGNExamples(sourceDir: string, testDir: string): Promise<Array<{
        filePath: string;
        relativePath: string;
        content: string;
        type: 'source' | 'test';
    }>> {
        Logger.info(`[ProjectStructureAnalyzer] 🔍 收集 GN 文件示例`);
        
        const examples: Array<{
            filePath: string;
            relativePath: string;
            content: string;
            type: 'source' | 'test';
        }> = [];
        
        // 收集源目录的 BUILD.gn
        const sourceGN = path.join(sourceDir, 'BUILD.gn');
        if (fs.existsSync(sourceGN)) {
            try {
                const content = fs.readFileSync(sourceGN, 'utf-8');
                examples.push({
                    filePath: sourceGN,
                    relativePath: path.relative(this.workspaceRoot, sourceGN),
                    content,
                    type: 'source'
                });
                Logger.info(`[ProjectStructureAnalyzer]    ✅ 源 GN: ${path.basename(sourceGN)}`);
            } catch (error) {
                Logger.warn(`[ProjectStructureAnalyzer]    ⚠️ 读取源 GN 失败: ${error}`);
            }
        }
        
        // 收集测试目录的 BUILD.gn
        if (fs.existsSync(testDir)) {
            const testGN = path.join(testDir, 'BUILD.gn');
            if (fs.existsSync(testGN)) {
                try {
                    const content = fs.readFileSync(testGN, 'utf-8');
                    examples.push({
                        filePath: testGN,
                        relativePath: path.relative(this.workspaceRoot, testGN),
                        content,
                        type: 'test'
                    });
                    Logger.info(`[ProjectStructureAnalyzer]    ✅ 测试 GN: ${path.basename(testGN)}`);
                } catch (error) {
                    Logger.warn(`[ProjectStructureAnalyzer]    ⚠️ 读取测试 GN 失败: ${error}`);
                }
            }
        }
        
        return examples;
    }
    
    /**
     * 分析项目模式
     */
    private analyzePatterns(structure: ProjectStructure): {
        testFileNaming: string;
        testDirectory: string;
        gnFileLocations: string[];
    } {
        Logger.info(`[ProjectStructureAnalyzer] 🔍 分析项目模式`);
        
        // 分析测试文件命名模式
        let testFileNaming = '*_test.cpp';  // 默认模式
        if (structure.existingTestExamples.length > 0) {
            const fileNames = structure.existingTestExamples.map(ex => ex.fileName);
            
            if (fileNames.every(name => name.endsWith('_test.cpp'))) {
                testFileNaming = '*_test.cpp';
            } else if (fileNames.every(name => name.endsWith('_unittest.cpp'))) {
                testFileNaming = '*_unittest.cpp';
            } else if (fileNames.every(name => name.startsWith('test_'))) {
                testFileNaming = 'test_*.cpp';
            }
        }
        
        // 分析测试目录模式
        const testDirectory = structure.directoryStructure.relativeTestDir;
        
        // 分析 GN 文件位置
        const gnFileLocations = structure.existingGNExamples.map(gn => gn.relativePath);
        
        Logger.info(`[ProjectStructureAnalyzer] 📋 模式分析结果:`);
        Logger.info(`[ProjectStructureAnalyzer]    测试文件命名: ${testFileNaming}`);
        Logger.info(`[ProjectStructureAnalyzer]    测试目录: ${testDirectory}`);
        Logger.info(`[ProjectStructureAnalyzer]    GN 文件位置: ${gnFileLocations.join(', ')}`);
        
        return {
            testFileNaming,
            testDirectory,
            gnFileLocations
        };
    }
    
    /**
     * 格式化项目结构为提示文本（传递给 LLM）
     */
    formatForLLM(structure: ProjectStructure): string {
        let prompt = `\n## 📊 项目结构信息\n\n`;
        
        // 1. 源文件信息
        prompt += `### 当前源文件\n`;
        prompt += `- 文件路径: \`${structure.sourceFile.relativePath}\`\n`;
        prompt += `- 所在目录: \`${structure.directoryStructure.relativeSourceDir}\`\n\n`;
        
        // 2. 目录结构
        prompt += `### 目录结构\n`;
        prompt += `- 源代码目录: \`${structure.directoryStructure.relativeSourceDir}\`\n`;
        prompt += `- 测试代码目录: \`${structure.directoryStructure.relativeTestDir}\`\n\n`;
        
        // 3. 项目规范
        prompt += `### 项目规范（从现有代码学习到的模式）\n`;
        prompt += `- 测试文件命名模式: \`${structure.patterns.testFileNaming}\`\n`;
        prompt += `- 测试文件应该放在: \`${structure.patterns.testDirectory}/\` 目录下\n\n`;
        
        // 4. 现有测试文件示例
        if (structure.existingTestExamples.length > 0) {
            prompt += `### 现有测试文件示例（参考这些文件的编写风格）\n\n`;
            
            for (const example of structure.existingTestExamples) {
                prompt += `#### 示例: \`${example.relativePath}\`\n\n`;
                
                if (example.content) {
                    // 只显示前 50 行，避免 prompt 过长
                    const lines = example.content.split('\n').slice(0, 50);
                    prompt += `\`\`\`cpp\n${lines.join('\n')}\n`;
                    if (example.content.split('\n').length > 50) {
                        prompt += `... (省略剩余内容)\n`;
                    }
                    prompt += `\`\`\`\n\n`;
                }
            }
        }
        
        // 5. GN 文件示例
        if (structure.existingGNExamples.length > 0) {
            prompt += `### GN 配置文件示例（参考这些文件的配置方式）\n\n`;
            
            for (const example of structure.existingGNExamples) {
                prompt += `#### ${example.type === 'source' ? '源文件 GN' : '测试文件 GN'}: \`${example.relativePath}\`\n\n`;
                prompt += `\`\`\`gn\n${example.content}\n\`\`\`\n\n`;
            }
        }
        
        prompt += `\n**请根据以上项目结构和规范，生成符合项目风格的测试代码。**\n\n`;
        
        return prompt;
    }
}

